package com.apes.framework.jpa.tuple.date;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;

import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;

/**
 * 功能：设置最近更新时间
 *
 * @author xul
 * @create 2018-01-16 20:55
 */
public class DateGeneration implements AnnotationValueGeneration<DateTime> {

    private GenerationTiming timing;
    protected ValueGenerator<?> generator;

    @Override
    public GenerationTiming getGenerationTiming() {
        return timing;
    }

    /**
     * Initializes this generation strategy for the given annotation instance.
     *
     * @param annotation   an instance of the strategy's annotation type. Typically implementations will retrieve the
     *                     annotation's attribute values and store them in fields.
     * @param propertyType the type of the property annotated with the generator annotation. Implementations may use
     *                     the type to determine the right {@link ValueGenerator} to be applied.
     * @throws HibernateException in case an error occurred during initialization, e.g. if
     *                            an implementation can't create a value for the given property type.
     */
    @Override
    public void initialize(DateTime annotation, Class<?> propertyType) {
        this.timing = annotation.value().getEquivalent();
        if (Date.class.isAssignableFrom(propertyType)) {
            generator = new CurrentDateGenerator();
        } else if (java.sql.Date.class.isAssignableFrom(propertyType)) {
            generator = new CurrentSqlDateGenerator();
        } else {
            throw new HibernateException("Unsupported property type for generator annotation " + annotation.getClass().getSimpleName());
        }
    }

//    @Override
//    public void initialize(A annotation, Class<?> propertyType) {
//        if (LocalDateTime.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> LocalDateTime.now();
//        } else if (LocalDate.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> LocalDate.now();
//        } else if (LocalTime.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> LocalTime.now();
//        } else if (java.sql.Date.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> new java.sql.Date(new Date().getTime());
//        } else if (Time.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> new Time(new Date().getTime());
//        } else if (Timestamp.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> new Timestamp(new Date().getTime());
//        } else if (Date.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> new Date();
//        } else if (Calendar.class.isAssignableFrom(propertyType)) {
//            generator = (session, owner) -> {
//                Calendar calendar = Calendar.getInstance();
//                calendar.setTime(new Date());
//                return calendar;
//            };
//        } else {
//            throw new HibernateException("Unsupported property type for generator annotation " + annotation.getClass().getSimpleName());
//        }
//    }

    /**
     * Obtain the in-VM value generator.
     * <p/>
     * May return {@code null}.  In fact for values that are generated "in the database" via execution of the
     * INSERT/UPDATE statement, the expectation is that {@code null} be returned here
     *
     * @return The strategy for performing in-VM value generation
     */
    @Override
    public ValueGenerator<?> getValueGenerator() {
        return generator;
    }

    /**
     * For values which are generated in the database ({@link #getValueGenerator()} == {@code null}), should the
     * column be referenced in the INSERT / UPDATE SQL?
     * <p/>
     * This will be false most often to have a DDL-defined DEFAULT value be applied on INSERT
     *
     * @return {@code true} indicates the column should be included in the SQL.
     */
    @Override
    public boolean referenceColumnInSql() {
        return false;
    }

    /**
     * For values which are generated in the database ({@link #getValueGenerator} == {@code null}), if the
     * column will be referenced in the SQL ({@link #referenceColumnInSql()} == {@code true}), what value should be
     * used in the SQL as the column value.
     * <p/>
     * Generally this will be a function call or a marker (DEFAULTS).
     * <p/>
     * NOTE : for in-VM generation, this will not be called and the column value will implicitly be a JDBC parameter ('?')
     *
     * @return The column value to be used in the SQL.
     */
    @Override
    public String getDatabaseGeneratedReferencedColumnValue() {
        return null;
    }


    class CurrentDateGenerator implements ValueGenerator<Date> {

        @Override
        public Date generateValue(Session session, Object owner) {
            return new Date();
        }
    }

    class CurrentCalendarGenerator implements ValueGenerator<Calendar> {

        @Override
        public Calendar generateValue(Session session, Object owner) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime( new Date() );
            return calendar;
        }
    }

    class CurrentSqlDateGenerator implements ValueGenerator<java.sql.Date> {

        @Override
        public java.sql.Date generateValue(Session session, Object owner) {
            return new java.sql.Date( new Date().getTime() );
        }
    }

    class CurrentSqlTimeGenerator implements ValueGenerator<Time> {

        @Override
        public Time generateValue(Session session, Object owner) {
            return new Time( new Date().getTime() );
        }
    }

    class CurrentSqlTimestampGenerator implements ValueGenerator<Timestamp> {

        @Override
        public Timestamp generateValue(Session session, Object owner) {
            return new Timestamp( new Date().getTime() );
        }
    }

}